package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.aliyun.GreenImageScan;
import com.heima.aliyun.GreenTextScan;
import com.heima.common.constants.message.PublishArticleConstants;
import com.heima.common.exception.CustException;
import com.heima.feigns.AdminFeign;
import com.heima.common.constants.wemedia.WemediaConstants;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.service.WmNewsAutoScanService;
import com.heima.wemedia.service.WmNewsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @authar by xiecong
 * @Date 2022/3/4
 * @Description
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {

    @Autowired
    WmNewsService wmNewsService;

    @Value("${file.oss.web-site}")
    String wesite;

    @Autowired
    RabbitTemplate rabbitTemplate;
    /**
     * 自媒体文章自动审核
     *
     * @param wmNewsId 自媒体文章id
     */
    @Override
    public void autoScanWmNews(Integer wmNewsId) {
        //1.校验参数文章id
        if (wmNewsId == null) {
            log.info("文章自动审核失败，原因：文章id不能为空{}", wmNewsId);
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.根据id查询文章信息
        WmNews wmNews = wmNewsService.getById(wmNewsId);
        if (wmNews == null) {
            log.info("文章自动审核失败，原因：未查询到自媒体文章信息，wmNewsId：{}", wmNewsId);
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }
        //3.检查文章状态  状态:1
        if (!WemediaConstants.WM_NEWS_SUMMIT_STATUS.equals(wmNews.getStatus())) {
            log.info("文章自动审核失败，原因：该文章的审核状态不为1，wmNewsId：{}", wmNewsId);
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不是审核状态");
        }
        //4.抽取文章的所有文本内容和图片url
        Map<String, Object> textAndImages = handleTextAndImages(wmNews);
        //5.文章中敏感词审核，如果不通过，状态改为2
        boolean isSensitive = handleSensitive((String) textAndImages.get("content"), wmNews);
        if (!isSensitive) {
            log.info("文章自动审核失败，内容中包含敏感词");
            return;
        }
        //6.阿里云文本审核  block:2 review:3
        boolean isTextScan = handleTextScan((String) textAndImages.get("content"), wmNews);
        if (!isTextScan) {
            log.info("文章自动审核失败，内容中包含违规信息");
            return;
        }
        //7.阿里云图片审核  block:2 review:3
        List<String> images = (List<String>) textAndImages.get("image");
        //判断内容是否有图
        if (!CollectionUtils.isEmpty(images)) {
           boolean isImageScan = handleImageScan(images,wmNews);
            if (!isImageScan) {
                log.info("文章自动审核失败，图片中包含违规信息");
                return;
            }
        }
        //8.修改文章状态：8
        updateWmNews(wmNews,WmNews.Status.SUCCESS.getCode(),"审核通过");

        // TODO 9.发布延时任务，用于发布文章
        //5. 通知定时发布文章
        // 发布时间
        long publishTime = wmNews.getPublishTime().getTime();
        // 当前时间
        long nowTime = new Date().getTime();
        long remainTime = publishTime - nowTime;
        // 发布文章
        rabbitTemplate.convertAndSend(PublishArticleConstants.DELAY_DIRECT_EXCHANGE
                , PublishArticleConstants.PUBLISH_ARTICLE_ROUTE_KEY
                , wmNews.getId()
                , (message)->{                              // 延时消息 必设置
                    message.getMessageProperties().setHeader("x-delay",remainTime<=0?0:remainTime);
                    return message;
                }
        );
        log.info("立即发布文章通知成功发送，文章id : {}", wmNews.getId());
    }
    @Autowired
    GreenImageScan greenImageScan;
    /**
     * 基于阿里云内容安全，审核图片是否违规
     * @param images
     * @param wmNews
     * @return
     */
    private boolean handleImageScan(List<String> images, WmNews wmNews) {
        boolean flag = false;
        try {
            Map map = greenImageScan.imageUrlScan(images);
            String suggestion = (String) map.get("suggestion");
            switch (suggestion){
                case "block":
                    updateWmNews(wmNews,WmNews.Status.FAIL.getCode(), "图片违规");
                    break;
                case "review":
                    updateWmNews(wmNews,WmNews.Status.ADMIN_AUTH.getCode(), "图片中含有不确定因素");
                case "pass":
                    flag = true;
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
            updateWmNews(wmNews,WmNews.Status.ADMIN_AUTH.getCode(),"阿里云审核调用失败，转为人工审核");
        }
        return flag;
    }

    @Autowired
    GreenTextScan greenTextScan;

    /**
     * 基于阿里云内容安全，审核文本是否违规
     *
     * @param content
     * @param wmNews
     * @return
     */
    private boolean handleTextScan(String content, WmNews wmNews) {
        boolean flag = false;
        try {
            Map map = greenTextScan.greenTextScan(content);
            String suggestion = (String) map.get("suggestion");
            switch (suggestion) {
                case "block":
                    updateWmNews(wmNews, WmNews.Status.FAIL.getCode(), "内容中包含违规信息");
                    break;
                case "review":
                    updateWmNews(wmNews, WmNews.Status.ADMIN_AUTH.getCode(), "内容中包含不确定信息，转为人工审核");
                    break;
                case "pass":
                    flag = true;
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
            updateWmNews(wmNews, WmNews.Status.ADMIN_AUTH.getCode(), "阿里云审核调用失败，转为人工审核");

        }
        return flag;
    }

    @Autowired
    AdminFeign adminFeign;

    /**
     * 基于DFA审核敏感词
     *
     * @param content
     * @param wmNews
     * @return
     */
    private boolean handleSensitive(String content, WmNews wmNews) {
        boolean flag = true;
        //1.远程查询敏感词列表
        ResponseResult<List<String>> sensitivesResult = adminFeign.sensitives();
        if (!sensitivesResult.checkCode()) {
            CustException.cust(AppHttpCodeEnum.REMOTE_SERVER_ERROR);
        }
        List<String> sensitives = sensitivesResult.getData();
        if (!CollectionUtils.isEmpty(sensitives)) {
            //2.基于DFA审核内容中是否包含敏感词
            //将敏感词列表封装成DFA结构
            SensitiveWordUtil.initMap(sensitives);
            // 检查敏感词，返回map集合中
            Map<String, Integer> map = SensitiveWordUtil.matchWords(content);
            //3.如果包含敏感词，将文章状态改为2，设置原因
            //如果map中有数据，说明包含敏感词
            if (!CollectionUtils.isEmpty(map)) {
                updateWmNews(wmNews, WmNews.Status.FAIL.getCode(), "内容中包含敏感词" + map);
                flag = false;
            }

        }
        return flag;
    }

    /**
     * 修改文章状态
     *
     * @param wmNews
     * @param status
     * @param reason
     */
    private void updateWmNews(WmNews wmNews, short status, String reason) {
        wmNews.setStatus(status);
        wmNews.setReason(reason);
        wmNewsService.updateById(wmNews);
    }

    /**
     * 抽取文章中所有文本和图片（素材图片+封面图片）
     *
     * @param wmNews
     * @return content：string  images：图片集合
     */
    private Map<String, Object> handleTextAndImages(WmNews wmNews) {
        //获取文章
        String contents = wmNews.getContent();
        //文章转成集合，Map的json类型数据
        List<Map> contentMap = JSON.parseArray(contents, Map.class);
        //1.抽取文章中内容
        //筛选type=text,获取value,拼接成字符串
        String content = contentMap.stream()
                .filter(m -> "test".equals(m.get("type")))
                .map(m -> (String) m.get("value"))
                .collect(Collectors.joining(","));
        //文本和标题拼接成字符串
        content += "," + wmNews.getTitle();
        //2.抽取文章中图片
        //晒衣内容中的图片image，获取value，将图片收集到集合中
        List<String> images = contentMap.stream()
                .filter(m -> "test".equals(m.get("image")))
                .map(m -> (String) m.get("value"))
                .collect(Collectors.toList());
        //3.封面上的图片(Str  url1,url2...)
        String imagesStr = wmNews.getImages();
        //判断封面是否为空，如果不为空
        if (StringUtils.isNotBlank(imagesStr)) {
            //将封面字符串用逗号切割
            String[] split = imagesStr.split(",");
            //用流将封面url加上前缀，收集到集合
            List<String> imageList = Arrays.stream(split)
                    .map(url -> wesite + url)
                    .collect(Collectors.toList());
            //用addAll将图片集合和封面集合加起来
            images.addAll(imageList);
        }
        //素材去重(图片+封面)，覆盖
        images = images.stream().distinct().collect(Collectors.toList());
        //4.用Map封装返回结果
        HashMap<String, Object> map = new HashMap<>();
        map.put("content", content);
        map.put("images", images);
        //返回
        return map;
    }
}
